#define PrynInternal
#define PrynImplementation
#include <pryn.h>
#include <stdlib.h>

extern PrynResult prynLibraryLoadPlatform (PrynLibrary *library);
extern PrynResult prynLibraryDestroyPlatform (PrynLibrary *library);

// Build a function for a library, ensuring it's non-zero before executing body, and assigns state.
#define PrynLibraryBody(BODY) \
	do { \
		PrynStartFunction (); \
		{ \
			prynAssert (library, NullArgument); \
			state = library->pState; \
			BODY \
		} \
		PrynEndFunction (); \
	} while (0)

#define PrynLibraryGetProperty(NAME, TYPE, INITIAL, VALUE) \
	PrynExport (PrynResult, prynLibrary##NAME, (PrynLibrary *library, TYPE *output)) \
	{ \
		const PrynString nullString = {0, 0}; \
		if (output) *output = INITIAL; \
		PrynLibraryBody ({ \
			prynAssert (output, NullArgument); \
			*output = VALUE; \
		}); \
	}

PrynLibraryGetProperty (State, PrynState *, 0, state)
PrynLibraryGetProperty (Tags, PrynTagList *, 0, &library->pCommonObject.pTags)
PrynLibraryGetProperty (Name, PrynString, nullString, library->pCommonResource.pName)
PrynLibraryGetProperty (Description, PrynString, nullString, library->pCommonResource.pDescription)

PrynExport (PrynResult, prynLibraryCreate, (PrynLibrary **output, PrynState *state, const PrynString *filename))
{
	PrynStartFunctionHasState ();
	PrynLibrary *library;
	
	if (output) *output = 0;
	prynAssert (state && output && filename, NullArgument);
	
	for (library = state->pLibraries.first; library; library = library->pNext)
		if (!prynStringEqual (&library->pFilename, filename))
			break;
		
	if (!library)
	{
		prynCall (prynAllocateClearedObject (state, &library));
		library->pCommonObject.pType = PrynObjectTypeLibrary;
		library->pState = state;
		prynStringDuplicate (&library->pFilename, state, filename);
		prynDListAppendP (state->pLibraries, library);
	}
	
	if (output) *output = library;
	PrynEndFunction ();
}

PrynExport (PrynResult, prynLibraryLoad, (PrynLibrary *library))
{
	return prynLibraryLoadPlatform (library);
}

PrynExport (PrynResult, prynLibrarySetName, (PrynLibrary *library, const PrynString *value))
{
	PrynLibraryBody ({
		prynAssert (value, NullArgument);
		prynCommonResourceSetName (&library->pCommonResource, value);
	});
}

PrynExport (PrynResult, prynLibrarySetDescription, (PrynLibrary *library, const PrynString *value))
{
	PrynLibraryBody ({
		prynAssert (value, NullArgument);
		prynCommonResourceSetDescription (&library->pCommonResource, value);
	});
}

PrynExport (PrynResult, prynLibraryInPath, (PrynLibrary *library, const PrynString *pathPointer))
{
	PrynStartFunction ();
	PrynString filename, path;

	prynAssert (library && pathPointer, NullArgument);
	state = library->pState;
	filename = library->pFilename;
	path = *pathPointer;

	if (path.length && (path.pointer [path.length - 1] == '/' || path.pointer [path.length - 1] == '\\'))
		path.length --;

	if (!PrynIsTrue (prynStringStartsWith (&filename, &path))
		|| filename.length == path.length
		|| (filename.pointer [path.length] != '/' && filename.pointer [path.length] != '\\'))
		prynReturnDone ();

	PrynEndFunction ();
}

PrynExport (PrynResult, prynLibraryDestroy, (PrynLibrary *library))
{
	PrynStartFunction ();
	PrynType *type, *typeNext;
	PrynFactory *factory, *factoryNext;
	PrynMediator *mediator, *mediatorNext;

	prynAssert (library, NullArgument);
	state = library->pState;

	prynCommonObjectDestroy (&library->pCommonObject, state);
	prynLibraryDestroyPlatform (library);

	for (type = state->pTypes.first; type; type = typeNext)
	{
		typeNext = type->pNext;
		if (type->pLibrary == library)
			prynTypeDestroy (type);
	}

	for (factory = state->pFactories.first; factory; factory = factoryNext)
	{
		factoryNext = factory->pNext;
		if (factory->pLibrary == library)
			prynFactoryDestroy (factory);
	}

	for (mediator = state->pMediators.first; mediator; mediator = mediatorNext)
	{
		mediatorNext = mediator->pNext;
		if (mediator->pLibrary == library)
			prynMediatorDestroy (mediator);
	}

	prynStringFree (&library->pFilename, state);
	prynCommonResourceDestroy (&library->pCommonResource, state);
	prynDListRemoveP (state->pLibraries, library);
	prynFree (state, library);

	PrynEndFunction ();
}